#include "ContentFile.hpp"

#include <stdexcept>
#include <sstream>
#include <iostream>

namespace se{

//local functions
uint64_t getFileSize(std::ifstream& file){
	file.seekg(0, std::ios::end);
	uint64_t end = file.tellg();
	
	file.seekg(0, std::ios::beg);
	uint64_t beg = file.tellg();
	
	return end - beg;
}


void ContentFile::loadDataFileFromFile(DataFile& target, char* filename, uint32_t contentRef){
	std::ifstream fileToOpen;
	fileToOpen.exceptions(std::ifstream::goodbit);
	fileToOpen.open(filename,std::ios_base::in|std::ios_base::binary);
	//if the opening failed
	if(!fileToOpen.is_open()){
		std::stringstream ss;
		ss<<"ContentFile::GetFromDebug(\""<<filename<<"\","<<contentRef<<") failed. file coudn't be opened.";
		throw(std::invalid_argument(ss.str()));
	}
	
	// Getting the size.
	FileSize_t fileToOpenSize = getFileSize(fileToOpen);
	// what will contain the data 
	//(for vc, why the max array size should be 2**32 ? I want my 64 back !).
	char* fileContent = new char[(uint32_t)fileToOpenSize];
	
	try{
		//loading the file content in memory
		fileToOpen.read(fileContent,(std::streamsize)fileToOpenSize);
		//putting it in a DataFile
		target.Load(fileContent, fileToOpenSize, true);
	}catch(...){
		delete fileContent;
		throw;
	}
}
//methods

ContentFile::ContentFile(const char* filePath):
	m_opened(false),
	m_file(),
	m_filetype(),
	m_blockSize(),
	m_filesize(){
	Open(filePath);
}


void ContentFile::Open(const char* filePath){
	m_file.exceptions( std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit );
	try{
	m_file.open(filePath,std::ios_base::in|std::ios_base::binary);
	}catch(...){
		printf("ERROR\n");
	}
	if(!m_file.is_open()){
		std::stringstream ss;
		ss<<"ContentFile::ContentFile("<<filePath<<") failed. file coudn't be opened.";
		throw(std::invalid_argument(ss.str()));
	}
	try{
		m_filesize = getFileSize(m_file);
		
		testFileTypeIsContentFile(filePath);
		
		char fileType = 0;
		m_file.read((char*)&fileType, 1);
		
		if(fileType=='d'){
			//opening a debug-mode file
			m_filetype = 'd';
		}else if(fileType=='R'){
			//opening a release Raw file (no file names table, simpler initialisation)
			m_filetype = 'R';
			try{
				m_file.read((char*)&m_blockSize, 4);
			}catch(std::exception&){
				std::stringstream ss;
				ss<<"ContentFile::ContentFile("<<filePath<<") failed. not a Raw release data file.";
				throw(std::invalid_argument(ss.str()));
			}
		}else{
			std::stringstream ss;
			ss<<"ContentFile::ContentFile("<<filePath<<") failed. file type not handled : '"<< fileType <<"'.";
			throw(std::invalid_argument(ss.str()));
		}
	}catch(...){
		m_file.close();
		throw;
	}
	
	m_opened = true;//reached only if there where no errors thrown
}


void ContentFile::Close(){
	m_file.close();
	m_opened = false;
}


bool ContentFile::IsOpened() const{
	return m_opened;
}


DataFile& ContentFile::Get(DataFile& target, uint32_t contentRef){
	if(m_filetype=='d'){
		return GetFromDebug(target,contentRef);
	}else if(m_filetype=='R'){
		return GetFromRawRelease(target,contentRef);
	}else{
		return target;//this is not supposed to happen
	}
}


DataFile& ContentFile::GetFromDebug(DataFile& target, uint32_t contentRef){
	//jumping the type check
	m_file.seekg(5, std::ios::beg);
	
	//FILENAME_MAX might not be appropriated. should set a lower limit
	char buffer[FILENAME_MAX];
	for(uint32_t i = 0;i<=contentRef;i++){
		m_file.getline(buffer,FILENAME_MAX);
	}
	
	//opening the file
	loadDataFileFromFile(target,buffer, contentRef);
	return target;
}


DataFile& ContentFile::GetFromRawRelease(DataFile& target, uint32_t contentRef){
	
	//the file position
	m_file.seekg(8+4*contentRef, std::ios::beg);
	uint32_t fileToOpenPos = 0;
	m_file.read((char*)&fileToOpenPos, 4);
	
	//the file content : size
	m_file.seekg(m_blockSize*fileToOpenPos, std::ios::beg);
	uint32_t fileToOpenSize = 0;
	m_file.read((char*)&fileToOpenSize, 4);
	
	//the file content : data
	char* fileContent = new char[fileToOpenSize];
	try{
		m_file.read(fileContent,fileToOpenSize);
		
		target.Load(fileContent, fileToOpenSize, true);
	}catch(...){
		delete fileContent;
		throw;
	}
	return target;
}


ContentFile::~ContentFile(){
	m_file.close();
}

//private stuff

void ContentFile::testFileTypeIsContentFile(const char* filePath = ""){	
	if(m_filesize<4){
		std::stringstream ss;
		ss<<"ContentFile::ContentFile("<<filePath<<") failed. file is too small.";
		throw(std::invalid_argument(ss.str()));
	}
	uint32_t fileTypeCheck = 0;
	m_file.read((char*)&fileTypeCheck, 3);
	if(fileTypeCheck!=4604755){// Doing it like a man ! in fact, this is "SCF"
		std::stringstream ss;
		ss<<"ContentFile::ContentFile("<<filePath<<") failed. Wrong file type.";
		throw(std::invalid_argument(ss.str()));
	}
}

}//namespace se
